/********************************************************************* * * Copyright (C) 2002 Andrew Khan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***************************************************************************/ package jxl.read.biff; import java.util.ArrayList; import jxl.common.Assert; import jxl.common.Logger; import jxl.WorkbookSettings; import jxl.biff.BuiltInName; import jxl.biff.IntegerHelper; import jxl.biff.RecordData; import jxl.biff.StringHelper; /** * Holds an excel name record, and the details of the cells/ranges it refers * to */ public class NameRecord extends RecordData { /** * The logger */ private static Logger logger = Logger.getLogger(NameRecord.class); /** * The name */ private String name; /** * The built in name */ private BuiltInName builtInName; /** * The 0-based index in the name table */ private int index; /** * The 0-based index sheet reference for a record name * 0 is for a global reference */ private int sheetRef = 0; /** * Indicates whether this is a biff8 name record. Used during copying */ private boolean isbiff8; /** * Dummy indicators for overloading the constructor */ private static class Biff7 {}; public static Biff7 biff7 = new Biff7(); // Constants which refer to the name type private static final int commandMacro = 0xc; private static final int builtIn = 0x20; // Constants which refer to the parse tokens after the string private static final int cellReference = 0x3a; private static final int areaReference = 0x3b; private static final int subExpression = 0x29; private static final int union = 0x10; /** * A nested class to hold range information */ public class NameRange { /** * The first column */ private int columnFirst; /** * The first row */ private int rowFirst; /** * The last column */ private int columnLast; /** * The last row */ private int rowLast; /** * The first sheet */ private int externalSheet; /** * Constructor * * @param s1 the sheet * @param c1 the first column * @param r1 the first row * @param c2 the last column * @param r2 the last row */ NameRange(int s1, int c1, int r1, int c2, int r2) { columnFirst = c1; rowFirst = r1; columnLast = c2; rowLast = r2; externalSheet = s1; } /** * Accessor for the first column * * @return the index of the first column */ public int getFirstColumn() { return columnFirst; } /** * Accessor for the first row * * @return the index of the first row */ public int getFirstRow() { return rowFirst; } /** * Accessor for the last column * * @return the index of the last column */ public int getLastColumn() { return columnLast; } /** * Accessor for the last row * * @return the index of the last row */ public int getLastRow() { return rowLast; } /** * Accessor for the first sheet * * @return the index of the external sheet */ public int getExternalSheet() { return externalSheet; } } /** * The ranges referenced by this name */ private ArrayList ranges; /** * Constructs this object from the raw data * * @param t the raw data * @param ws the workbook settings * @param ind the index in the name table */ NameRecord(Record t, WorkbookSettings ws, int ind) { super(t); index = ind; isbiff8 = true; try { ranges = new ArrayList(); byte[] data = getRecord().getData(); int option = IntegerHelper.getInt(data[0], data[1]); int length = data[3]; sheetRef = IntegerHelper.getInt(data[8],data[9]); if ((option & builtIn) != 0) { builtInName = BuiltInName.getBuiltInName(data[15]); } else { name = StringHelper.getString(data, length, 15, ws); } if ((option & commandMacro) != 0) { // This is a command macro, so it has no cell references return; } int pos = length + 15; if (data[pos] == cellReference) { int sheet = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); int row = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); int columnMask = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); int column = columnMask & 0xff; // Check that we are not dealing with offsets Assert.verify((columnMask & 0xc0000) == 0); NameRange r = new NameRange(sheet, column, row, column, row); ranges.add(r); } else if (data[pos] == areaReference) { int sheet1 = 0; int r1 = 0; int columnMask = 0; int c1 = 0; int r2 = 0; int c2 = 0; NameRange range = null; while (pos < data.length) { sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); c1 = columnMask & 0xff; // Check that we are not dealing with offsets Assert.verify((columnMask & 0xc0000) == 0); columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); c2 = columnMask & 0xff; // Check that we are not dealing with offsets Assert.verify((columnMask & 0xc0000) == 0); range = new NameRange(sheet1, c1, r1, c2, r2); ranges.add(range); pos += 11; } } else if (data[pos] == subExpression) { int sheet1 = 0; int r1 = 0; int columnMask = 0; int c1 = 0; int r2 = 0; int c2 = 0; NameRange range = null; // Consume unnecessary parsed tokens if (pos < data.length && data[pos] != cellReference && data[pos] != areaReference) { if (data[pos] == subExpression) { pos += 3; } else if (data[pos] == union) { pos += 1; } } while (pos < data.length) { sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); c1 = columnMask & 0xff; // Check that we are not dealing with offsets Assert.verify((columnMask & 0xc0000) == 0); columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); c2 = columnMask & 0xff; // Check that we are not dealing with offsets Assert.verify((columnMask & 0xc0000) == 0); range = new NameRange(sheet1, c1, r1, c2, r2); ranges.add(range); pos += 11; // Consume unnecessary parsed tokens if (pos < data.length && data[pos] != cellReference && data[pos] != areaReference) { if (data[pos] == subExpression) { pos += 3; } else if (data[pos] == union) { pos += 1; } } } } else { String n = name != null ? name : builtInName.getName(); logger.warn("Cannot read name ranges for " + n + " - setting to empty"); NameRange range = new NameRange(0,0,0,0,0); ranges.add(range); } } catch (Throwable t1) { // Generate a warning // Names are really a nice to have, and we don't want to halt the // reading process for functionality that probably won't be used logger.warn("Cannot read name"); name = "ERROR"; } } /** * Constructs this object from the raw data * * @param t the raw data * @param ws the workbook settings * @param ind the index in the name table * @param dummy dummy parameter to indicate a biff7 workbook */ NameRecord(Record t, WorkbookSettings ws, int ind, Biff7 dummy) { super(t); index = ind; isbiff8 = false; try { ranges = new ArrayList(); byte[] data = getRecord().getData(); int length = data[3]; sheetRef = IntegerHelper.getInt(data[8], data[9]); name = StringHelper.getString(data, length, 14, ws); int pos = length + 14; if (pos >= data.length) { // There appears to be nothing after the name, so return return; } if (data[pos] == cellReference) { int sheet = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); int row = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); int column = data[pos + 17]; NameRange r = new NameRange(sheet, column, row, column, row); ranges.add(r); } else if (data[pos] == areaReference) { int sheet1 = 0; int r1 = 0; int c1 = 0; int r2 = 0; int c2 = 0; NameRange range = null; while (pos < data.length) { sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); c1 = data[pos + 19]; c2 = data[pos + 20]; range = new NameRange(sheet1, c1, r1, c2, r2); ranges.add(range); pos += 21; } } else if (data[pos] == subExpression) { int sheet1 = 0; int sheet2 = 0; int r1 = 0; int c1 = 0; int r2 = 0; int c2 = 0; NameRange range = null; // Consume unnecessary parsed tokens if (pos < data.length && data[pos] != cellReference && data[pos] != areaReference) { if (data[pos] == subExpression) { pos += 3; } else if (data[pos] == union) { pos += 1; } } while (pos < data.length) { sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); c1 = data[pos + 19]; c2 = data[pos + 20]; range = new NameRange(sheet1, c1, r1, c2, r2); ranges.add(range); pos += 21; // Consume unnecessary parsed tokens if (pos < data.length && data[pos] != cellReference && data[pos] != areaReference) { if (data[pos] == subExpression) { pos += 3; } else if (data[pos] == union) { pos += 1; } } } } } catch (Throwable t1) { // Generate a warning // Names are really a nice to have, and we don't want to halt the // reading process for functionality that probably won't be used logger.warn("Cannot read name."); name = "ERROR"; } } /** * Gets the name * * @return the strings */ public String getName() { return name; } /** * Gets the built in name * * @return the built in name */ public BuiltInName getBuiltInName() { return builtInName; } /** * Gets the array of ranges for this name. This method is public as it is * used from the writable side when copying ranges * * @return the ranges */ public NameRange[] getRanges() { NameRange[] nr = new NameRange[ranges.size()]; return (NameRange[]) ranges.toArray(nr); } /** * Accessor for the index into the name table * * @return the 0-based index into the name table */ int getIndex() { return index; } /** * The 0-based index sheet reference for a record name * 0 is for a global reference * * @return the sheet reference for name formula */ public int getSheetRef() { return sheetRef; } /** * Set the index sheet reference for a record name * 0 is for a global reference */ public void setSheetRef(int i) { sheetRef = i; } /** * Called when copying a sheet. Just returns the raw data * * @return the raw data */ public byte[] getData() { return getRecord().getData(); } /** * Called when copying to determine whether this is a biff8 name * * @return TRUE if this is a biff8 name record, FALSE otherwise */ public boolean isBiff8() { return isbiff8; } /** * Queries whether this is a global name or not * * @return TRUE if this is a global name, FALSE otherwise */ public boolean isGlobal() { return sheetRef == 0; } }